package com.hxx.widget

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.core.view.marginEnd
import androidx.core.view.marginStart

open class FlexLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0,
) : ViewGroup(context, attrs, defStyleAttr) {
    private var columnSize = 6
    private val rowCenterYPosition = arrayListOf<Int>()
    private val columnsWidths = arrayListOf<Int>()
    private var flexJustifyContent: FlexJustifyContent = FlexJustifyContent.NO

    init {
        val a = context.obtainStyledAttributes(attrs, R.styleable.FlexLayout)
        columnSize = a.getInt(R.styleable.FlexLayout_columnsSize, columnSize)
        when (a.getInt(R.styleable.FlexLayout_flexType, 0)) {
            0 -> flexJustifyContent = FlexJustifyContent.NO
            1 -> flexJustifyContent = FlexJustifyContent.BETWEEN
            2 -> flexJustifyContent = FlexJustifyContent.AROUND
            3 -> flexJustifyContent = FlexJustifyContent.CENTER
        }
        a.recycle()
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        calculateViewLayout()
    }

    private fun calculateViewLayout() {
        if (childCount == 0) return
        when (flexJustifyContent) {
            FlexJustifyContent.CENTER -> {
                var currentRowIndex = -1
                var startX = 0
                var startY = 0
                for (i in 0 until childCount) {
                    val child = getChildAt(i)
                    val rowIndex = i / columnSize
                    if (rowIndex != currentRowIndex) {
                        currentRowIndex++
                        startX = (measuredWidth - columnsWidths[currentRowIndex]) / 2
                        startY = rowCenterYPosition[currentRowIndex]
                    }
                    val left = startX + child.marginStart
                    val top = startY - child.measuredHeight / 2
                    val right = left + child.measuredWidth
                    val bottom = startY + child.measuredHeight / 2
                    child.layout(left, top, right, bottom)
                    startX = right + child.marginEnd
                }
            }
            FlexJustifyContent.AROUND -> {
                var currentRowIndex = -1
                var startX = 0
                var startY = 0
                var rowPadding = 0
                for (i in 0 until childCount) {
                    val child = getChildAt(i)
                    val rowIndex = i / columnSize
                    if (rowIndex != currentRowIndex) {
                        currentRowIndex++
                        val numRowIndex = currentRowIndex + 1
                        var columnsSize =
                            Math.min(numRowIndex * columnSize, childCount) % columnSize
                        if (columnsSize == 0) {
                            columnsSize = columnSize
                        }
                        rowPadding =
                            (measuredWidth - columnsWidths[currentRowIndex]) / columnsSize / 2
                        startX = 0
                        startY = rowCenterYPosition[currentRowIndex]
                    }
                    val left = startX + child.marginStart + rowPadding
                    val top = startY - child.measuredHeight / 2
                    val right = left + child.measuredWidth
                    val bottom = startY + child.measuredHeight / 2
                    child.layout(left, top, right, bottom)
                    startX = right + child.marginEnd + rowPadding
                }
            }
            FlexJustifyContent.BETWEEN -> {
                var currentRowIndex = -1
                var startX = 0
                var startY = 0
                var rowPadding = 0
                for (i in 0 until childCount) {
                    val child = getChildAt(i)
                    val rowIndex = i / columnSize
                    if (rowIndex != currentRowIndex) {
                        currentRowIndex++
                        val numRowIndex = currentRowIndex + 1
                        var columnsSize =
                            Math.min(numRowIndex * columnSize, childCount) % columnSize
                        if (columnsSize == 0) {
                            columnsSize = columnSize
                        }
                        if (columnsSize == 1) {
                            rowPadding = -1
                        } else if (columnsSize == 2) {
                            rowPadding = (measuredWidth - columnsWidths[currentRowIndex])
                        } else {
                            rowPadding =
                                (measuredWidth - columnsWidths[currentRowIndex]) / (columnsSize - 1)
                        }
                        startX = 0
                        startY = rowCenterYPosition[currentRowIndex]
                    }
                    val left = startX + child.marginStart
                    val top = startY - child.measuredHeight / 2
                    val right = left + child.measuredWidth
                    val bottom = startY + child.measuredHeight / 2
                    child.layout(left, top, right, bottom)
                    startX = right + child.marginEnd + rowPadding
                }
            }
            FlexJustifyContent.NO -> {
                val columnWidth = (measuredWidth - paddingStart - paddingEnd) / columnSize
                for (i in 0 until childCount) {
                    val child = getChildAt(i)
                    val rowIndex = i / columnSize
                    val columnIndex = i % columnSize
                    val startX = columnWidth * columnIndex + columnWidth / 2 + paddingStart
                    val startY = rowCenterYPosition[rowIndex]

                    val left = startX - child.measuredWidth / 2
                    val top = startY - child.measuredHeight / 2
                    val right = left + child.measuredWidth
                    val bottom = startY + child.measuredHeight / 2
                    child.layout(left, top, right, bottom)
                }
            }
        }

    }


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var horizontalChildNeed = 0
        var verticalChildNeed = 0
        val childMaxWidthMargin =
            (MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd) / columnSize
        val childMaxHeightMargin =
            MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom
        var rowIndex = 0
        var rowMaxHeight = 0
        var columnWidth = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val lp = child.layoutParams as FlexLayoutParams
            measureChildWithLimit(
                child,
                widthMeasureSpec,
                heightMeasureSpec,
                childMaxWidthMargin,
                childMaxHeightMargin
            )
            if (i / columnSize == rowIndex) {
                columnWidth += (child.measuredWidth + lp.marginStart + lp.marginEnd)
                rowMaxHeight =
                    Math.max(child.measuredHeight + lp.topMargin + lp.bottomMargin, rowMaxHeight)
            } else {
                rowCenterYPosition.add(rowIndex, paddingTop + rowMaxHeight / 2 + verticalChildNeed)
                columnsWidths.add(rowIndex, columnWidth)
                horizontalChildNeed = Math.max(columnWidth, horizontalChildNeed)
                columnWidth = (child.measuredWidth + lp.marginStart + lp.marginEnd)
                verticalChildNeed += rowMaxHeight
                rowIndex += 1
                rowMaxHeight = child.measuredHeight
            }
        }
        //记录每行需要得宽度
        columnsWidths.add(rowIndex, columnWidth)
        //记录每行的中心点位置
        rowCenterYPosition.add(rowIndex, paddingTop + rowMaxHeight / 2 + verticalChildNeed)
        verticalChildNeed += rowMaxHeight
        val width = Math.max(horizontalChildNeed, MeasureSpec.getSize(widthMeasureSpec))
        when (MeasureSpec.getMode(heightMeasureSpec)) {
            MeasureSpec.UNSPECIFIED -> {
                when (layoutParams.height) {
                    LayoutParams.MATCH_PARENT -> {
                        setMeasuredDimension(
                            width,
                            verticalChildNeed + paddingTop + paddingBottom
                        )
                    }
                    LayoutParams.WRAP_CONTENT -> {
                        setMeasuredDimension(width, verticalChildNeed + paddingTop + paddingBottom)
                    }
                    else -> {
                        setMeasuredDimension(width, layoutParams.height)
                    }
                }
            }
            MeasureSpec.AT_MOST -> {
                when (layoutParams.height) {
                    LayoutParams.MATCH_PARENT -> {
                        setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec))
                    }
                    LayoutParams.WRAP_CONTENT -> {
                        setMeasuredDimension(width,
                            Math.min(MeasureSpec.getSize(heightMeasureSpec),
                                verticalChildNeed + paddingTop + paddingBottom))
                    }
                    else -> {
                        setMeasuredDimension(width, layoutParams.height)
                    }
                }

            }
            MeasureSpec.EXACTLY -> {
                setMeasuredDimension(width, layoutParams.height)
            }
        }
    }

    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return FlexLayoutParams(context, attrs)
    }

    override fun generateLayoutParams(p: LayoutParams?): LayoutParams {
        return FlexLayoutParams(p)
    }

    override fun generateDefaultLayoutParams(): LayoutParams {
        return FlexLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
    }

    /**
     * 测量大小，限制每行的最大子视图个数
     */
    private fun measureChildWithLimit(
        child: View,
        parentWidthSpec: Int,
        parentHeightSpec: Int,
        maxChildWidth: Int,
        maxChildHeight: Int,
    ) {
        val lp = child.layoutParams as FlexLayoutParams
        val childWidthMeasureSpec = getChildMeasureSpecWithLimit(
            parentWidthSpec,
            lp.width,
            maxChildWidth - lp.marginStart - lp.marginEnd
        )
        val childHeightMeasureSpec = getChildMeasureSpecWithLimit(
            parentHeightSpec,
            lp.height,
            maxChildHeight - lp.topMargin - lp.bottomMargin
        )
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec)
    }

    private fun getChildMeasureSpecWithLimit(
        parentSpec: Int,
        childDimension: Int,
        childMaxLimit: Int,
    ): Int {
        val mode = MeasureSpec.getMode(parentSpec)
        var resultSize = 0.coerceAtLeast(childMaxLimit)
        var resultMode = mode
        when (mode) {
            MeasureSpec.EXACTLY -> when {
                childDimension >= 0 -> {
                    resultSize = childDimension
                    resultMode = MeasureSpec.EXACTLY
                }
                childDimension == LayoutParams.WRAP_CONTENT -> {
                    resultMode = MeasureSpec.AT_MOST
                }
                childDimension == LayoutParams.MATCH_PARENT -> {
                    resultMode = MeasureSpec.EXACTLY
                }
            }
            MeasureSpec.AT_MOST -> when {
                childDimension >= 0 -> {
                    resultSize = childDimension.coerceAtMost(resultSize)
                    resultMode = MeasureSpec.EXACTLY
                }
                childDimension == LayoutParams.WRAP_CONTENT -> {
                    resultMode = MeasureSpec.AT_MOST
                }
                childDimension == LayoutParams.MATCH_PARENT -> {
                    resultMode = MeasureSpec.EXACTLY
                }
            }
            MeasureSpec.UNSPECIFIED -> when {
                childDimension >= 0 -> {
                    resultSize = childDimension
                    resultMode = MeasureSpec.EXACTLY
                }
                childDimension == LayoutParams.WRAP_CONTENT -> {
                    resultSize = 0
                    resultMode = MeasureSpec.UNSPECIFIED
                }
                childDimension == LayoutParams.MATCH_PARENT -> {
                    resultSize = 0
                    resultMode = MeasureSpec.UNSPECIFIED
                }
            }
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode)
    }
}

class FlexLayoutParams : ViewGroup.MarginLayoutParams {
    constructor(c: Context?, attrs: AttributeSet?) : super(c, attrs)
    constructor(width: Int, height: Int) : super(width, height)
    constructor(source: ViewGroup.MarginLayoutParams?) : super(source)
    constructor(source: ViewGroup.LayoutParams?) : super(source)
}